#pragma once

#include "IR.h"
#include "IRBuilder.h"
#include "SysYBaseVisitor.h"
#include "SysYParser.h"
#include <cassert>
#include <forward_list>
#include <string>
#include <stack>

namespace sysy {

class CircleStack {
  private: 
    std::stack<BasicBlock *> startStack;
    std::stack<BasicBlock *> exitStack;
  public:
    BasicBlock *getStartBlock() {
      return startStack.top();
    }
    BasicBlock *getExitBlock() {
      return exitStack.top();
    }
    void popStartBlock() {
      return startStack.pop();
    }
    void popExitBlock() {
      return exitStack.pop();
    }
    void pushStartBlock(BasicBlock *lst) {
      startStack.push(lst);
    }
    void pushExitBlock(BasicBlock *lst) {
      exitStack.push(lst);
    }
}; // class CircleStack

class SysYIRGenerator : public SysYBaseVisitor {
private:
  std::unique_ptr <CompUnit> compUnit;
  IRBuilder builder;
  // SymbolTable symbols;
  CircleStack circle;
public:
  SysYIRGenerator() = default;

  CompUnit *get() const { return compUnit.get(); }

public:
  virtual std::any visitCompUnit(SysYParser::CompUnitContext *ctx) override;
 
  virtual std::any visitDecl(SysYParser::DeclContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitConstDecl(SysYParser::ConstDeclContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }
  
  virtual std::any visitBType(SysYParser::BTypeContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any
  visitConstDef(SysYParser::ConstDefContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any
  visitConstInitVal(SysYParser::ConstInitValContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitVarDecl(SysYParser::VarDeclContext *ctx) override;
  // {

  //   return visitChildren(ctx);
  // }

  virtual std::any visitVarDef(SysYParser::VarDefContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }


  virtual std::any
  visitInitVal(SysYParser::InitValContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitFuncDef(SysYParser::FuncDefContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  
  virtual std::any visitFuncType(SysYParser::FuncTypeContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }



  virtual std::any
  visitFuncFParams(SysYParser::FuncFParamsContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any
  visitFuncFParam(SysYParser::FuncFParamContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitBlock(SysYParser::BlockContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }
  virtual std::any visitBlockItem(SysYParser::BlockItemContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitStmt(SysYParser::StmtContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any
  visitAssignStmt(SysYParser::AssignStmtContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitExpStmt(SysYParser::ExpStmtContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitIfStmt(SysYParser::IfStmtContext *ctx) override;
    

  virtual std::any visitWhileStmt(SysYParser::WhileStmtContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitBreakStmt(SysYParser::BreakStmtContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any
  visitContinueStmt(SysYParser::ContinueStmtContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any
  visitReturnStmt(SysYParser::ReturnStmtContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitExp(SysYParser::ExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitCond(SysYParser::CondContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitLVal(SysYParser::LValContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }


  virtual std::any 
  visitPrimaryExp(SysYParser::PrimaryExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitNumber(SysYParser::NumberContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitUnaryExp(SysYParser::UnaryExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitUnaryOp(SysYParser::UnaryOpContext *ctx) override {
    // Need not to do it...
    return visitChildren(ctx);
  }

  virtual std::any visitFuncRParams(SysYParser::FuncRParamsContext *ctx) override;
  // {
  //   // Need not to do it...
  //   return visitChildren(ctx);
  // }

  virtual std::any visitMulExp(SysYParser::MulExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any
  visitAddExp(SysYParser::AddExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitRelExp(SysYParser::RelExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitEqExp(SysYParser::EqExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitLAndExp(SysYParser::LAndExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitLOrExp(SysYParser::LOrExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitConstExp(SysYParser::ConstExpContext *ctx) override;
  // {
  //   return visitChildren(ctx);
  // }

  virtual std::any visitCall(SysYParser::CallContext *ctx) override;

private:
  std::any visitGlobalDecl(SysYParser::DeclContext *ctx);
  std::any visitLocalDecl(SysYParser::DeclContext *ctx);
  Type *getArithmeticResultType(Type *lhs, Type *rhs) {
    assert(lhs->isIntOrFloat() and rhs->isIntOrFloat());
    return lhs == rhs ? lhs : Type::getFloatType();
  }
}; // class SysYIRGenerator

} // namespace sysy